package workflow

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"shqsoft.cn/pasnj/base"
	"shqsoft.cn/pasnj/popedom"
	"shqsoft.cn/pasnj/workflow/entity"

	"github.com/jinzhu/gorm"
)

// Create 创建流程
func Create(wfid int, userID int, tx *gorm.DB) (runID int, err error) {
	wf := GetWorkFlow(wfid)
	if wf == nil {
		err = fmt.Errorf("指定的流程ID:%d在流程配置中不存在！", wfid)
		return
	}
	act := wf.GetAction(1)
	if act == nil {
		err = fmt.Errorf("指定的stepID:%d在流程%s配置中不存在！", 1, wf.Name)
		return
	}
	roleArr := strings.Split(act.Executor, ",")
	isok := false
	for _, roleStr := range roleArr {
		roleID, err := strconv.Atoi(roleStr)
		if err != nil {
			return 0, fmt.Errorf("指定的执行人%s错误，必须为int，多个以逗号分开！", act.Executor)
		}
		isok, err = popedom.IsHasRole(userID, roleID)
		if err != nil {
			return 0, err
		}
		if isok {
			break
		}
	}
	if !isok {
		err = fmt.Errorf("您没有创建%s流程的权限，请联系管理员", wf.Name)
		return
	}
	run := entity.FlowRun{
		WFID:       wfid,
		Step:       1,
		Createtime: time.Now(),
		State:      "0",
	}
	canTx := false
	if tx == nil {
		canTx = true
		tx = base.DB.Begin()
	}
	if err = tx.Create(&run).Error; err != nil {
		if canTx {
			tx.Rollback()
		}
		return
	}
	if err = createStep(tx, run.ID, 1, []int{userID}); err != nil {
		if canTx {
			tx.Rollback()
		}
		return
	}
	cb := callBackPool[fmt.Sprintf("create_%d", wfid)]
	if cb != nil {
		if err = cb(tx, run.ID, userID); err != nil {
			if canTx {
				tx.Rollback()
			}
			return
		}
	}
	if act.IsAuto {
		if err := SubmitWithTX(run.ID, userID, "", "1", tx, 0); err != nil {
			if canTx {
				tx.Rollback()
			}
			return 0, err
		}
	}
	if canTx {
		tx.Commit()
	}
	return run.ID, err
}

// Abort 中止
func Abort(runID int, userID int) (err error) {
	tx := base.DB.Begin()
	err = AbortWithTx(runID, userID, tx)
	if err != nil {
		tx.Rollback()
	}
	tx.Commit()
	return
}

// AbortWithTx 中止流程
func AbortWithTx(runID int, userID int, tx *gorm.DB) (err error) {
	run := entity.FlowRun{}
	if err = tx.Find(&run, runID).Error; err != nil {
		return
	}
	if run.State == "1" {
		return errors.New("流程运行完成，不能中止！")
	}
	if run.State == "2" {
		return errors.New("流程运行已经中止，不能再次中止！")
	}
	wf := GetWorkFlow(run.WFID)
	isMg := wf.IsManager(userID)
	runSteps := []entity.FlowRunStep{}
	whereEnt := entity.FlowRunStep{RunID: runID, StepID: run.Step, EMID: userID}
	err = tx.Where(whereEnt).Where("STATE in (0,1)").Find(&runSteps).Error

	if err != nil {
		return
	}
	lenrs := len(runSteps)
	if isMg {
		if lenrs == 0 {
			act := wf.GetAction(run.Step)
			if !act.CanManager {
				return fmt.Errorf("%s不是可管理任务不能中止！", act.Desc)
			}
			now := time.Now()
			whereEnt.StartTime = time.Now()
			whereEnt.SubmitTime = &now
			whereEnt.State = "2"
			whereEnt.Description = "中止"
			if err = tx.Create(&whereEnt).Error; err != nil {
				return
			}
		}
	} else {
		if lenrs == 0 {
			return fmt.Errorf("不是在办任务，不能中止！")
		}
	}
	if err = tx.Model(entity.FlowRun{}).Where("ID=?", runID).Update("STATE", "2").Error; err != nil {
		return
	}
	if err = tx.Model(entity.FlowRunStep{}).Where("RUNID=? AND StepID=? AND EMID<>?", runID, run.Step, userID).Update("STATE", "3").Error; err != nil {
		return
	}
	if lenrs > 0 {
		// if err = tx.Model(entity.FlowRunStep{}).Where(whereEnt).Update("STATE", "2").Error; err != nil {
		// 	return
		// }
		now := time.Now()
		if err = tx.Model(entity.FlowRunStep{}).Where(whereEnt).Update(entity.FlowRunStep{State: "2", SubmitTime: &now, SubmitType: "5"}).Error; err != nil {
			return
		}
	}

	return
}

// Delete 删除流程
func Delete(runID int) (err error) {
	tx := base.DB.Begin()
	err = DeleteWithTx(runID, tx)
	if err != nil {
		tx.Rollback()
	}
	tx.Commit()
	return nil
}

// DeleteWithTx 删除流程
func DeleteWithTx(runID int, tx *gorm.DB) (err error) {
	run := entity.FlowRun{}
	if err = tx.Find(&run, runID).Error; err != nil {
		return
	}
	if run.State == "1" {
		return errors.New("流程运行完成，不能删除！")
	}
	if err = tx.Where("ID=?", runID).Delete(entity.FlowRun{}).Error; err != nil {
		return
	}
	if err = tx.Where("RUNID=?", runID).Delete(entity.FlowRunStep{}).Error; err != nil {
		return
	}
	return
}

func Submit(runID int, userID int, desc, subType string) (err error) {
	tx := base.DB.Begin()
	ishld := 0
	err = SubmitWithTX(runID, userID, desc, subType, tx, ishld)
	if err != nil {
		tx.Rollback()
	}
	tx.Commit()
	return
}

func SubmitWithTX(runID int, userID int, desc, subType string, tx *gorm.DB, ishld int) (err error) {
	run := entity.FlowRun{}
	if err = tx.Find(&run, runID).Error; err != nil {
		return
	}
	if run.State == "1" {
		return errors.New("流程运行完成，不能提交！")
	}
	if run.State == "2" {
		return errors.New("流程运行已经中止，不能提交！")
	}
	wf := GetWorkFlow(run.WFID)
	if wf == nil {
		return fmt.Errorf("指定的流程ID:%d在流程配置中不存在！", run.WFID)
	}
	act := wf.GetAction(run.Step)
	if act == nil {
		return fmt.Errorf("指定的stepID:%d在流程%s配置中不存在！", run.Step, wf.Name)
	}
	cb := callBackPool[fmt.Sprintf("beforeSubmit_%d_%d", run.WFID, run.Step)]
	if cb != nil {
		if err = cb(tx, run.ID, userID); err != nil {
			return
		}
	}
	nextStep := run.Step
	if subType != "4" {
		nextStep = run.Step + 1
	}
	if act.End {
		run.State = "1"
		nextStep = run.Step
	}

	isMg := wf.IsManager(userID)
	runSteps := []entity.FlowRunStep{}
	whereEnt := entity.FlowRunStep{RunID: runID, StepID: run.Step, EMID: userID}
	err = tx.Where(whereEnt).Where("STATE in (0,1)").Find(&runSteps).Error
	if err != nil {
		return
	}
	var ok bool
	lenrs := len(runSteps)
	if isMg {
		if lenrs == 0 {
			act := wf.GetAction(run.Step)
			if !act.CanManager {
				return fmt.Errorf("%s不是可管理任务不能提交！", act.Desc)
			}
			whereEnt.StartTime = time.Now()
			whereEnt.State = "1"
			whereEnt.Description = "管理员强制提交"
			if err = tx.Create(&whereEnt).Error; err != nil {
				return
			}
			subType := "6"
			if ok, err = finishStep(tx, run.ID, run.Step, userID, nextStep, subType, desc, act.ConSign && !isMg); err != nil {
				return
			}
		} else {
			if lenrs == 0 {
				return fmt.Errorf("不是在办任务，不能提交！")
			} else {
				if ok, err = finishStep(tx, run.ID, run.Step, userID, nextStep, subType, desc, act.ConSign && !isMg); err != nil {
					return
				}
			}
		}
	} else {
		if lenrs == 0 {
			return fmt.Errorf("不是在办任务，不能提交！")
		} else {
			if ok, err = finishStep(tx, run.ID, run.Step, userID, nextStep, subType, desc, act.ConSign && !isMg); err != nil {
				return
			}
		}
	}

	if act.ConSign && !ok {
		return nil
	}
	run.Step = nextStep
	if err = tx.Save(run).Error; err != nil {
		return
	}
	if act.End {
		cb := callBackPool[fmt.Sprintf("finish_%d", run.WFID)]
		if cb != nil {
			if err = cb(tx, run.ID, userID); err != nil {
				return
			}
		}
		return nil
	}
	act = wf.GetAction(nextStep)
	if act == nil {
		return fmt.Errorf("指定的stepID:%d在流程%s配置中不存在！", nextStep, wf.Name)
	}
	var count int
	err = tx.Model(entity.FlowRunStep{}).Where(entity.FlowRunStep{RunID: runID, StepID: nextStep}).Count(&count).Error
	var userIDs []int
	var exclUserIDs []int
	if act.Appointer == "" || count > 0 {
		userIDs, exclUserIDs, _, err = getExecutor(tx, runID, nextStep, true)
		if err != nil {
			return err
		}
		if len(userIDs) == 0 {
			return fmt.Errorf(`流程“%s”运行到节点“%s”，没有找到执行人`, wf.Name, act.Desc)
		}
		if subType != "4" {
			if err = createStep(tx, run.ID, nextStep, userIDs); err != nil {
				return
			}
		}
		var exclUserIDs2 []int
		err = tx.Table("t_sysuser").Where("(SysUserName like '%公共%' or SysUserName like '%离职%' or STATUS='2' or YGXZ='2') and id in (?) and id<>?", userIDs, userID).Pluck("ID", &exclUserIDs2).Error
		if err != nil {
			return
		}
		exclUserIDs = append(exclUserIDs, exclUserIDs2...)
	}
	if act.IsAuto && len(userIDs) > 0 {
		if err := SubmitWithTX(run.ID, userIDs[0], "系统自动提交", "1", tx, ishld); err != nil {
			return err
		}
	}
	if base.IsInSliceForInt(userIDs, userID) {
		if err := SubmitWithTX(run.ID, userID, "", "1", tx, ishld); err != nil {
			return err
		}
	}
	if act.ConSign {
		for _, uid := range exclUserIDs {
			if err := SubmitWithTX(run.ID, uid, "系统自动提交", "1", tx, ishld); err != nil {
				return err
			}
		}
	} else if len(exclUserIDs) > 0 {
		if err := SubmitWithTX(run.ID, exclUserIDs[0], "系统自动提交", "1", tx, ishld); err != nil {
			return err
		}
	}
	if subType == "4" {
		AbortWithTx(runID, userID, tx)
	}
	err = tx.Exec("UPDATE T_FLOW_RUN_STEP SET ISHLDSP=? WHERE RUNID=?", ishld, run.ID).Error
	if err != nil {
		return err
	}
	if err != nil {
		return err
	}
	if wf.ID == 1 {
		tx.Exec("UPDATE T_INDI_CLIE_DIVI3 SET UPDATETIME=? WHERE FRID=?", time.Now().Format("2006-01-02 15:04:05"), runID)
	} else if wf.ID == 2 {
		tx.Exec("UPDATE T_INDI_CLIE_DIVI2 SET UPDATETIME=? WHERE FRID=?", time.Now().Format("2006-01-02 15:04:05"), runID)
	} else if wf.ID == 3 {
		tx.Exec("UPDATE T_COMP_CLIE_DIVI SET UPDATETIME=? WHERE FRID=?", time.Now().Format("2006-01-02 15:04:05"), runID)
	} else if wf.ID == 4 {
		tx.Exec("UPDATE T_COMP_CLIE_DIVI2 SET UPDATETIME=? WHERE FRID=?", time.Now().Format("2006-01-02 15:04:05"), runID)
	} else if wf.ID == 5 {
		tx.Exec("UPDATE T_CONF_SJXQ SET UPDATETIME=? WHERE FRID=?", time.Now().Format("2006-01-02 15:04:05"), runID)
	}
	return nil
}

// SendBack 退回
func SendBack(runID int, userID int, backStep int, desc string) (err error) {
	run := entity.FlowRun{}
	tx := base.DB.Begin()
	if err = tx.Find(&run, runID).Error; err != nil {
		tx.Rollback()
		return
	}
	if run.State == "1" {
		tx.Rollback()
		return errors.New("流程运行完成，不能中止！")
	}
	if run.State == "2" {
		tx.Rollback()
		return errors.New("流程运行已经中止，不能再次中止！")
	}
	wf := GetWorkFlow(run.WFID)
	if wf == nil {
		tx.Rollback()
		return fmt.Errorf("指定的流程ID:%d在流程配置中不存在！", run.WFID)
	}
	act := wf.GetAction(run.Step)
	if act == nil {
		tx.Rollback()
		return fmt.Errorf("指定的stepID:%d在流程%s配置中不存在！", run.Step, wf.Name)
	}
	if !act.CanBack {
		tx.Rollback()
		return fmt.Errorf(`流程“%s”中的节点“%s”不允许退回！`, wf.Name, act.Desc)
	}
	var ok bool
	if ok, err = finishStep(tx, run.ID, run.Step, userID, backStep, "2", desc, act.ConSign); err != nil {
		tx.Rollback()
		return
	}
	if ok {
		run.Step = backStep
		if err = tx.Save(run).Error; err != nil {
			tx.Rollback()
			return
		}
	}
	var userIDs []int
	userIDs, _, _, err = getExecutor(tx, runID, backStep, true)
	if err != nil {
		tx.Rollback()
		return err
	}
	if err = createStep(tx, run.ID, backStep, userIDs); err != nil {
		tx.Rollback()
		return
	}
	tx.Commit()
	return nil
}
